package net.minecraft.client.gui;

import java.awt.Toolkit;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;

import com.darkmagician6.eventapi.EventManager;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import me.CheerioFX.FusionX.FusionX;
import me.CheerioFX.FusionX.events.EventGuiClose;
import me.CheerioFX.FusionX.events.EventGuiOpen;
import me.CheerioFX.FusionX.utils.GuiUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.stream.GuiTwitchUserMode;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.entity.RenderItem;
import net.minecraft.entity.EntityList;
import net.minecraft.event.ClickEvent;
import net.minecraft.event.HoverEvent;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.JsonToNBT;
import net.minecraft.nbt.NBTException;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.stats.Achievement;
import net.minecraft.stats.StatBase;
import net.minecraft.stats.StatList;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import tv.twitch.chat.ChatUserInfo;

public abstract class GuiScreen extends Gui implements GuiYesNoCallback {
	private static final Logger field_175287_a = LogManager.getLogger();
	private static final Set field_175284_f = Sets.newHashSet(new String[] { "http", "https" });
	private static final Splitter field_175285_g = Splitter.on('\n');

	/** Reference to the Minecraft object. */
	protected Minecraft mc;

	/**
	 * Holds a instance of RenderItem, used to draw the achievement icons on
	 * screen (is based on ItemStack)
	 */
	protected RenderItem itemRender;

	/** The width of the screen object. */
	public int width;

	/** The height of the screen object. */
	public int height;

	/** A list of all the buttons in this container. */
	protected List<GuiButton> buttonList = Lists.newArrayList();

	/** A list of all the labels in this container. */
	protected List labelList = Lists.newArrayList();
	public boolean allowUserInput;

	/** The FontRenderer used by GuiScreen */
	protected FontRenderer fontRendererObj;

	/** The button that was just pressed. */
	private GuiButton selectedButton;
	protected int eventButton;
	private long lastMouseEvent;

	/**
	 * Incremented when the game is in touchscreen mode and the screen is
	 * tapped, decremented if the screen isn't tapped. Does not appear to be
	 * used.
	 */
	private int touchValue;
	private URI field_175286_t;
	private static final String __OBFID = "CL_00000710";

	/**
	 * Draws the screen and all the components in it. Args : mouseX, mouseY,
	 * renderPartialTicks
	 */
	public void drawScreen(int mouseX, int mouseY, float partialTicks) {
		int var4;

		for (var4 = 0; var4 < this.buttonList.size(); ++var4) {
			this.buttonList.get(var4).drawButton(this.mc, mouseX, mouseY);
		}

		for (var4 = 0; var4 < this.labelList.size(); ++var4) {
			((GuiLabel) this.labelList.get(var4)).drawLabel(this.mc, mouseX, mouseY);
		}
	}

	/**
	 * Fired when a key is typed (except F11 who toggle full screen). This is
	 * the equivalent of KeyListener.keyTyped(KeyEvent e). Args : character
	 * (character on the key), keyCode (lwjgl Keyboard key code)
	 */
	protected void keyTyped(char typedChar, int keyCode) throws IOException {
		if (keyCode == 1) {
			this.mc.displayGuiScreen((GuiScreen) null);

			if (this.mc.currentScreen == null) {
				this.mc.setIngameFocus();
			}
		}
	}

	/**
	 * Returns a string stored in the system clipboard.
	 */
	public static String getClipboardString() {
		try {
			Transferable var0 = Toolkit.getDefaultToolkit().getSystemClipboard().getContents((Object) null);

			if (var0 != null && var0.isDataFlavorSupported(DataFlavor.stringFlavor)) {
				return (String) var0.getTransferData(DataFlavor.stringFlavor);
			}
		} catch (Exception var1) {
			;
		}

		return "";
	}

	/**
	 * Stores the given string in the system clipboard
	 */
	public static void setClipboardString(String copyText) {
		if (!StringUtils.isEmpty(copyText)) {
			try {
				StringSelection var1 = new StringSelection(copyText);
				Toolkit.getDefaultToolkit().getSystemClipboard().setContents(var1, (ClipboardOwner) null);
			} catch (Exception var2) {
				;
			}
		}
	}

	protected void renderToolTip(ItemStack itemIn, int x, int y) {
		List var4 = itemIn.getTooltip(this.mc.thePlayer, this.mc.gameSettings.advancedItemTooltips);

		for (int var5 = 0; var5 < var4.size(); ++var5) {
			if (var5 == 0) {
				var4.set(var5, itemIn.getRarity().rarityColor + (String) var4.get(var5));
			} else {
				var4.set(var5, EnumChatFormatting.GRAY + (String) var4.get(var5));
			}
		}

		this.drawHoveringText(var4, x, y);
	}

	/**
	 * Draws the text when mouse is over creative inventory tab. Params: current
	 * creative tab to be checked, current mouse x position, current mouse y
	 * position.
	 */
	protected void drawCreativeTabHoveringText(String tabName, int mouseX, int mouseY) {
		this.drawHoveringText(Arrays.asList(new String[] { tabName }), mouseX, mouseY);
	}

	protected void drawHoveringText(List textLines, int x, int y) {
		if (!textLines.isEmpty()) {
			GlStateManager.disableRescaleNormal();
			RenderHelper.disableStandardItemLighting();
			GlStateManager.disableLighting();
			GlStateManager.disableDepth();
			int var4 = 0;
			Iterator var5 = textLines.iterator();

			while (var5.hasNext()) {
				String var6 = (String) var5.next();
				int var7 = this.fontRendererObj.getStringWidth(var6);

				if (var7 > var4) {
					var4 = var7;
				}
			}

			int var14 = x + 12;
			int var15 = y - 12;
			int var8 = 8;

			if (textLines.size() > 1) {
				var8 += 2 + (textLines.size() - 1) * 10;
			}

			if (var14 + var4 > this.width) {
				var14 -= 28 + var4;
			}

			if (var15 + var8 + 6 > this.height) {
				var15 = this.height - var8 - 6;
			}

			this.zLevel = 300.0F;
			this.itemRender.zLevel = 300.0F;
			int var9 = -267386864;
			this.drawGradientRect(var14 - 3, var15 - 4, var14 + var4 + 3, var15 - 3, var9, var9);
			this.drawGradientRect(var14 - 3, var15 + var8 + 3, var14 + var4 + 3, var15 + var8 + 4, var9, var9);
			this.drawGradientRect(var14 - 3, var15 - 3, var14 + var4 + 3, var15 + var8 + 3, var9, var9);
			this.drawGradientRect(var14 - 4, var15 - 3, var14 - 3, var15 + var8 + 3, var9, var9);
			this.drawGradientRect(var14 + var4 + 3, var15 - 3, var14 + var4 + 4, var15 + var8 + 3, var9, var9);
			int var10 = 1347420415;
			int var11 = (var10 & 16711422) >> 1 | var10 & -16777216;
			this.drawGradientRect(var14 - 3, var15 - 3 + 1, var14 - 3 + 1, var15 + var8 + 3 - 1, var10, var11);
			this.drawGradientRect(var14 + var4 + 2, var15 - 3 + 1, var14 + var4 + 3, var15 + var8 + 3 - 1, var10,
					var11);
			this.drawGradientRect(var14 - 3, var15 - 3, var14 + var4 + 3, var15 - 3 + 1, var10, var10);
			this.drawGradientRect(var14 - 3, var15 + var8 + 2, var14 + var4 + 3, var15 + var8 + 3, var11, var11);

			for (int var12 = 0; var12 < textLines.size(); ++var12) {
				String var13 = (String) textLines.get(var12);
				this.fontRendererObj.func_175063_a(var13, var14, var15, -1);

				if (var12 == 0) {
					var15 += 2;
				}

				var15 += 10;
			}

			this.zLevel = 0.0F;
			this.itemRender.zLevel = 0.0F;
			GlStateManager.enableLighting();
			GlStateManager.enableDepth();
			RenderHelper.enableStandardItemLighting();
			GlStateManager.enableRescaleNormal();
		}
	}

	protected void func_175272_a(IChatComponent p_175272_1_, int p_175272_2_, int p_175272_3_) {
		if (p_175272_1_ != null && p_175272_1_.getChatStyle().getChatHoverEvent() != null) {
			HoverEvent var4 = p_175272_1_.getChatStyle().getChatHoverEvent();

			if (var4.getAction() == HoverEvent.Action.SHOW_ITEM) {
				ItemStack var5 = null;

				try {
					NBTTagCompound var6 = JsonToNBT.func_180713_a(var4.getValue().getUnformattedText());

					if (var6 instanceof NBTTagCompound) {
						var5 = ItemStack.loadItemStackFromNBT(var6);
					}
				} catch (NBTException var11) {
					;
				}

				if (var5 != null) {
					this.renderToolTip(var5, p_175272_2_, p_175272_3_);
				} else {
					this.drawCreativeTabHoveringText(EnumChatFormatting.RED + "Invalid Item!", p_175272_2_,
							p_175272_3_);
				}
			} else {
				String var8;

				if (var4.getAction() == HoverEvent.Action.SHOW_ENTITY) {
					if (this.mc.gameSettings.advancedItemTooltips) {
						try {
							NBTTagCompound var12 = JsonToNBT.func_180713_a(var4.getValue().getUnformattedText());

							if (var12 instanceof NBTTagCompound) {
								ArrayList var14 = Lists.newArrayList();
								NBTTagCompound var7 = var12;
								var14.add(var7.getString("name"));

								if (var7.hasKey("type", 8)) {
									var8 = var7.getString("type");
									var14.add("Type: " + var8 + " (" + EntityList.func_180122_a(var8) + ")");
								}

								var14.add(var7.getString("id"));
								this.drawHoveringText(var14, p_175272_2_, p_175272_3_);
							} else {
								this.drawCreativeTabHoveringText(EnumChatFormatting.RED + "Invalid Entity!",
										p_175272_2_, p_175272_3_);
							}
						} catch (NBTException var10) {
							this.drawCreativeTabHoveringText(EnumChatFormatting.RED + "Invalid Entity!", p_175272_2_,
									p_175272_3_);
						}
					}
				} else if (var4.getAction() == HoverEvent.Action.SHOW_TEXT) {
					this.drawHoveringText(field_175285_g.splitToList(var4.getValue().getFormattedText()), p_175272_2_,
							p_175272_3_);
				} else if (var4.getAction() == HoverEvent.Action.SHOW_ACHIEVEMENT) {
					StatBase var13 = StatList.getOneShotStat(var4.getValue().getUnformattedText());

					if (var13 != null) {
						IChatComponent var15 = var13.getStatName();
						ChatComponentTranslation var16 = new ChatComponentTranslation(
								"stats.tooltip.type." + (var13.isAchievement() ? "achievement" : "statistic"),
								new Object[0]);
						var16.getChatStyle().setItalic(Boolean.valueOf(true));
						var8 = var13 instanceof Achievement ? ((Achievement) var13).getDescription() : null;
						ArrayList var9 = Lists
								.newArrayList(new String[] { var15.getFormattedText(), var16.getFormattedText() });

						if (var8 != null) {
							var9.addAll(this.fontRendererObj.listFormattedStringToWidth(var8, 150));
						}

						this.drawHoveringText(var9, p_175272_2_, p_175272_3_);
					} else {
						this.drawCreativeTabHoveringText(EnumChatFormatting.RED + "Invalid statistic/achievement!",
								p_175272_2_, p_175272_3_);
					}
				}
			}

			GlStateManager.disableLighting();
		}
	}

	protected void func_175274_a(String p_175274_1_, boolean p_175274_2_) {
	}

	protected boolean func_175276_a(IChatComponent p_175276_1_) {
		if (p_175276_1_ == null) {
			return false;
		} else {
			ClickEvent var2 = p_175276_1_.getChatStyle().getChatClickEvent();

			if (isShiftKeyDown()) {
				if (p_175276_1_.getChatStyle().getInsertion() != null) {
					this.func_175274_a(p_175276_1_.getChatStyle().getInsertion(), false);
				}
			} else if (var2 != null) {
				URI var3;

				if (var2.getAction() == ClickEvent.Action.OPEN_URL) {
					if (!this.mc.gameSettings.chatLinks) {
						return false;
					}

					try {
						var3 = new URI(var2.getValue());

						if (!field_175284_f.contains(var3.getScheme().toLowerCase())) {
							throw new URISyntaxException(var2.getValue(),
									"Unsupported protocol: " + var3.getScheme().toLowerCase());
						}

						if (this.mc.gameSettings.chatLinksPrompt) {
							this.field_175286_t = var3;
							this.mc.displayGuiScreen(new GuiConfirmOpenLink(this, var2.getValue(), 31102009, false));
						} else {
							this.func_175282_a(var3);
						}
					} catch (URISyntaxException var4) {
						field_175287_a.error("Can\'t open url for " + var2, var4);
					}
				} else if (var2.getAction() == ClickEvent.Action.OPEN_FILE) {
					var3 = (new File(var2.getValue())).toURI();
					this.func_175282_a(var3);
				} else if (var2.getAction() == ClickEvent.Action.SUGGEST_COMMAND) {
					this.func_175274_a(var2.getValue(), true);
				} else if (var2.getAction() == ClickEvent.Action.RUN_COMMAND) {
					this.func_175281_b(var2.getValue(), false);
				} else if (var2.getAction() == ClickEvent.Action.TWITCH_USER_INFO) {
					ChatUserInfo var5 = this.mc.getTwitchStream().func_152926_a(var2.getValue());

					if (var5 != null) {
						this.mc.displayGuiScreen(new GuiTwitchUserMode(this.mc.getTwitchStream(), var5));
					} else {
						field_175287_a.error("Tried to handle twitch user but couldn\'t find them!");
					}
				} else {
					field_175287_a.error("Don\'t know how to handle " + var2);
				}

				return true;
			}

			return false;
		}
	}

	public void func_175275_f(String p_175275_1_) {
		this.func_175281_b(p_175275_1_, true);
	}

	public void func_175281_b(String p_175281_1_, boolean p_175281_2_) {
		if (p_175281_2_) {
			this.mc.ingameGUI.getChatGUI().addToSentMessages(p_175281_1_);
		}

		this.mc.thePlayer.sendChatMessage(p_175281_1_);
	}

	/**
	 * Called when the mouse is clicked. Args : mouseX, mouseY, clickedButton
	 */
	protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
		if (mouseButton == 0) {
			for (int var4 = 0; var4 < this.buttonList.size(); ++var4) {
				GuiButton var5 = this.buttonList.get(var4);

				if (var5.mousePressed(this.mc, mouseX, mouseY)) {
					this.selectedButton = var5;
					var5.playPressSound(this.mc.getSoundHandler());
					this.actionPerformed(var5);
				}
			}
		}
	}

	/**
	 * Called when a mouse button is released. Args : mouseX, mouseY,
	 * releaseButton
	 */
	protected void mouseReleased(int mouseX, int mouseY, int state) {
		if (this.selectedButton != null && state == 0) {
			this.selectedButton.mouseReleased(mouseX, mouseY);
			this.selectedButton = null;
		}
	}

	/**
	 * Called when a mouse button is pressed and the mouse is moved around.
	 * Parameters are : mouseX, mouseY, lastButtonClicked & timeSinceMouseClick.
	 */
	protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) {
	}

	protected void actionPerformed(GuiButton button) throws IOException {
	}

	/**
	 * Causes the screen to lay out its subcomponents again. This is the
	 * equivalent of the Java call Container.validate()
	 */
	public void setWorldAndResolution(Minecraft mc, int width, int height) {
		this.mc = mc;
		this.itemRender = mc.getRenderItem();
		this.fontRendererObj = mc.fontRendererObj;
		this.width = width;
		this.height = height;
		this.buttonList.clear();
		this.initGui();
	}

	/**
	 * Adds the buttons (and other controls) to the screen in question.
	 */
	public void initGui() {
		final EventGuiOpen event = new EventGuiOpen();
		EventManager.call(event);
		if (event.isCancelled()) {
			return;
		}
		GuiUtils.setInGui(true);
		FusionX.renderTheGui = true;
	}

	/**
	 * Delegates mouse and keyboard input.
	 */
	public void handleInput() throws IOException {
		if (Mouse.isCreated()) {
			while (Mouse.next()) {
				this.handleMouseInput();
			}
		}

		if (Keyboard.isCreated()) {
			while (Keyboard.next()) {
				this.handleKeyboardInput();
			}
		}
	}

	/**
	 * Handles mouse input.
	 */
	public void handleMouseInput() throws IOException {
		int var1 = Mouse.getEventX() * this.width / this.mc.displayWidth;
		int var2 = this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1;
		int var3 = Mouse.getEventButton();

		if (Mouse.getEventButtonState()) {
			if (this.mc.gameSettings.touchscreen && this.touchValue++ > 0) {
				return;
			}

			this.eventButton = var3;
			this.lastMouseEvent = Minecraft.getSystemTime();
			this.mouseClicked(var1, var2, this.eventButton);
		} else if (var3 != -1) {
			if (this.mc.gameSettings.touchscreen && --this.touchValue > 0) {
				return;
			}

			this.eventButton = -1;
			this.mouseReleased(var1, var2, var3);
		} else if (this.eventButton != -1 && this.lastMouseEvent > 0L) {
			long var4 = Minecraft.getSystemTime() - this.lastMouseEvent;
			this.mouseClickMove(var1, var2, this.eventButton, var4);
		}
	}

	/**
	 * Handles keyboard input.
	 */
	public void handleKeyboardInput() throws IOException {
		if (Keyboard.getEventKeyState()) {
			this.keyTyped(Keyboard.getEventCharacter(), Keyboard.getEventKey());
		}

		this.mc.dispatchKeypresses();
	}

	/**
	 * Called from the main game loop to update the screen.
	 */
	public void updateScreen() {
		final EventGuiOpen event = new EventGuiOpen();
		if (event.isCancelled()) {
			return;
		}
		GuiUtils.setInGui(true);
		FusionX.renderTheGui = true;
	}

	/**
	 * Called when the screen is unloaded. Used to disable keyboard repeat
	 * events
	 */
	public void onGuiClosed() {
		final EventGuiClose event = new EventGuiClose();
		EventManager.call(event);
		if (event.isCancelled()) {
			return;
		}
		GuiUtils.setInGui(false);
		FusionX.renderTheGui = true;
	}

	/**
	 * Draws either a gradient over the background screen (when it exists) or a
	 * flat gradient over background.png
	 */
	public void drawDefaultBackground() {
		this.drawWorldBackground(0);
	}

	public void drawWorldBackground(int tint) {
		if (this.mc.theWorld != null) {
			this.drawGradientRect(0, 0, this.width, this.height, -1072689136, -804253680);
		} else {
			this.drawBackground(tint);
		}
	}

	/**
	 * Draws the background (i is always 0 as of 1.2.2)
	 */
	public void drawBackground(int tint) {
		GlStateManager.disableLighting();
		GlStateManager.disableFog();
		Tessellator var2 = Tessellator.getInstance();
		WorldRenderer var3 = var2.getWorldRenderer();
		this.mc.getTextureManager().bindTexture(optionsBackground);
		GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
		float var4 = 32.0F;
		var3.startDrawingQuads();
		var3.func_178991_c(4210752);
		var3.addVertexWithUV(0.0D, this.height, 0.0D, 0.0D, this.height / var4 + tint);
		var3.addVertexWithUV(this.width, this.height, 0.0D, this.width / var4, this.height / var4 + tint);
		var3.addVertexWithUV(this.width, 0.0D, 0.0D, this.width / var4, tint);
		var3.addVertexWithUV(0.0D, 0.0D, 0.0D, 0.0D, tint);
		var2.draw();
	}

	/**
	 * Returns true if this GUI should pause the game when it is displayed in
	 * single-player
	 */
	public boolean doesGuiPauseGame() {
		return true;
	}

	@Override
	public void confirmClicked(boolean result, int id) {
		if (id == 31102009) {
			if (result) {
				this.func_175282_a(this.field_175286_t);
			}

			this.field_175286_t = null;
			this.mc.displayGuiScreen(this);
		}
	}

	private void func_175282_a(URI p_175282_1_) {
		try {
			Class var2 = Class.forName("java.awt.Desktop");
			Object var3 = var2.getMethod("getDesktop", new Class[0]).invoke((Object) null, new Object[0]);
			var2.getMethod("browse", new Class[] { URI.class }).invoke(var3, new Object[] { p_175282_1_ });
		} catch (Throwable var4) {
			field_175287_a.error("Couldn\'t open link", var4);
		}
	}

	/**
	 * Returns true if either windows ctrl key is down or if either mac meta key
	 * is down
	 */
	public static boolean isCtrlKeyDown() {
		return Minecraft.isRunningOnMac ? Keyboard.isKeyDown(219) || Keyboard.isKeyDown(220)
				: Keyboard.isKeyDown(29) || Keyboard.isKeyDown(157);
	}

	/**
	 * Returns true if either shift key is down
	 */
	public static boolean isShiftKeyDown() {
		return Keyboard.isKeyDown(42) || Keyboard.isKeyDown(54);
	}

	public static boolean func_175283_s() {
		return Keyboard.isKeyDown(56) || Keyboard.isKeyDown(184);
	}

	public static boolean func_175277_d(int p_175277_0_) {
		return p_175277_0_ == 45 && isCtrlKeyDown();
	}

	public static boolean func_175279_e(int p_175279_0_) {
		return p_175279_0_ == 47 && isCtrlKeyDown();
	}

	public static boolean func_175280_f(int p_175280_0_) {
		return p_175280_0_ == 46 && isCtrlKeyDown();
	}

	public static boolean func_175278_g(int p_175278_0_) {
		return p_175278_0_ == 30 && isCtrlKeyDown();
	}

	public void func_175273_b(Minecraft mcIn, int p_175273_2_, int p_175273_3_) {
		this.setWorldAndResolution(mcIn, p_175273_2_, p_175273_3_);
	}
}
